<?php
/**
 * Created by PhpStorm.
 * User: wangxj
 * Date: 2017/7/13
 * Time: 下午1:47
 */

namespace App\Helper;

use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter\LineFormatter;

class LogHelper
{
    const ENABLE_LOG = 1;

    private static $msSetting = ['file','daily'];
    private static $msLevel   = ['debug','info','notice','warning','error','critical','alert','emergency'];

    public static $msInstance = null;
    public static $msLogger    = null;
    public static $msFormatter = null;
    public static $msRotating  = [];
    public static $msStream    = [];

    private $mMethod   = '';
    private $mClass    = '';
    private $mLine     = 0;
    private $mIp       = '';
    private $mFile     = '';
    private $mLevel    = '';
    private $mDaily    = 1;
    private $mfullData = 0;

    //************************************************************
    //**                                                        **
    //**                                                        **
    //**                  公有方法                               **
    //**                                                        **
    //**                                                        **
    //************************************************************
    //写入数据
    public function _writeLog($logData1, $logData2 = null)
    {
        //控制是否需要记录log
        if (self::ENABLE_LOG != 1)
        {
            return;
        }

        try
        {
            $isCanPutLog = $this->getLogSetting();

            if ($isCanPutLog == 1)
            {
                if (empty($logData1))
                {
                    return;
                }

                //参数转字符串
                $logData1 = json_encode($logData1);
                if ($logData2 === null)
                {
                    $logFullData = $logData1;
                }
                else
                {
                    $logData2 = json_encode($logData2);
                    $logFullData = $logData1 . ':' . $logData2;
                }

                //组合显示文字
                if(empty($this->ip))
                {
                    $logFullData = "[$this->mClass::$this->mMethod][$this->mLine] " . $logFullData;
                }
                else
                {
                    $logFullData = "[$this->ip][$this->mClass::$this->mMethod][$this->mLine] " . $logFullData;
                }


                $this->writeData($logFullData);
            }
        }
        catch (\Exception $e)
        {
        }
    }

    //设置文件名
    public function _file($file)
    {
        $this->mFile = $file;

        return $this;
    }

    //是否是每天一个文件
    public function _daily($daily)
    {
        $this->mDaily = $daily;

        return $this;
    }

    //取得ip地址
    public function _getClientIp()
    {
        if (isset($_SERVER['REMOTE_ADDR']))
        {
            $cip = $_SERVER['REMOTE_ADDR'];
        }
        elseif (getenv("REMOTE_ADDR"))
        {
            $cip = getenv("REMOTE_ADDR");
        }
        elseif (getenv("HTTP_CLIENT_IP"))
        {
            $cip = getenv("HTTP_CLIENT_IP");
        }
        else
        {
            $cip = "";
        }

        return $cip;
    }

    //************************************************************
    //**                                                        **
    //**                                                        **
    //**                  私有方法                               **
    //**                                                        **
    //**                                                        **
    //************************************************************
    //单例模式
    private function __clone(){}
    private function __construct(){}

    //根据配置文件，判断是否需要输出log
    private function getLogSetting()
    {
        $fileName = dirname(public_path()) . "/debug_config.json";

        $isCanPutLog    = 0;
        do{
            if(empty($this->mClass) || empty($this->mMethod))
            {
                break;
            }

            //检查设置文件是否存在
            if(!file_exists($fileName))
            {
                break;
            }

            try {
                //
                //取得配置文件
                //
                $debugConfig = json_decode(file_get_contents($fileName ),true);

                //是否关闭log
                if(isset($debugConfig['enableLog']))
                {
                    if($debugConfig['enableLog'] == 0)
                    {
                        break;
                    }
                }

                //
                //检查Ip地址
                //
                if(!empty($this->ip) && !isset($debugConfig['ipList']))
                {
                    if(!in_array("*",$debugConfig['ipList']) && !in_array($this->ip,$debugConfig['ipList']))
                    {
                        break;
                    }
                }

                if(!empty($debugConfig))
                {
                    if(isset($debugConfig['methodList']))
                    {
                        $isCanPutLog = $this->isCanPutMethod($debugConfig['methodList']);
                    }
                }

                //是否关闭log
                if(isset($debugConfig['fullData']))
                {
                    $this->mfullData = $debugConfig['fullData'];
                }
            }
            catch (\Exception $e)
            {
                break;
            }

        }while(0);

        return $isCanPutLog;
    }

    //写log到文件
    private function writeData($data)
    {
        $file  = $this->mFile;
        $level = $this->mLevel;
        $daily = $this->mDaily;

        if (empty(self::$msLogger))
        {
            self::$msLogger = new Logger('xlogger');
        }

        if (empty(self::$msFormatter))
        {
            $logFormat         = "[%datetime%]%message%\n\n";
            self::$msFormatter = new LineFormatter($logFormat);;
        }

        $logFile  = storage_path(). "/logs/{$file}.log";

        if($daily > 0)
        {
            if(!isset(self::$msRotating[$file]))
            {
                self::$msRotating[$file] = new RotatingFileHandler($logFile);
            }
            $handler = self::$msRotating[$file];
        }
        else
        {
            if(!isset(self::$msStream[$file]))
            {
                self::$msStream[$file] = new StreamHandler($logFile);
            }
            $handler = self::$msStream[$file];
        }

        //设定显示模式
        $handler->setFormatter(self::$msFormatter);

        //清除之前的handler数据
        if(!empty(self::$msLogger->getHandlers()))
        {
            self::$msLogger->popHandler();
        }
        //设置新的handler
        self::$msLogger->pushHandler($handler);

        //写入数据
        self::$msLogger->{$level}($data);
    }

    //判断是否能写log到文件
    private function isCanPutMethod($methodList)
    {
        $isCanPut = 0;
        //
        //所有都记录
        //
        if(in_array("*",$methodList) || in_array("*::*",$methodList))
        {
            $isCanPut = 1;
        }
        else
        {
            //检查了或者方法
            //
            if(!empty($this->class) && !empty($this->method))
            {
                $classAll  = "*::" . $this->method;
                $methodAll = $this->class ."::*";
                $classMethod = $this->class . '::' .$this->method;

                if( in_array($classAll, $methodList) ||
                    in_array($methodAll, $methodList) ||
                    in_array($classMethod, $methodList)
                )
                {
                    $isCanPut = 1;
                }

            }
        }

        return $isCanPut;
    }

    //设置文件写入参数
    private function setPutParameter($method,$backtrace)
    {
        self::$msInstance->mLevel = $method;
        if (empty(self::$msInstance->mFile))
        {
            self::$msInstance->mFile = $method;
        }

        $backtrace_line = array_shift($backtrace); // 哪一行调用的log方法
        $backtrace_call = array_shift($backtrace); // 谁调用的log方法

        $line   = $backtrace_line['line'];
        $method = $backtrace_call['function'];

        $classFull = isset($backtrace_call['class']) ? $backtrace_call['class'] : '';
        $classArray = explode("\\",$classFull);
        $class = end($classArray) ?: '';

        self::$msInstance->mLine    = $line;
        self::$msInstance->mMethod  = $method;
        self::$msInstance->mClass   = $class;
        self::$msInstance->mIp      = self::$msInstance->_getClientIp();
    }

    //************************************************************
    //**                                                        **
    //**                                                        **
    //**                  魔术方法                               **
    //**                                                        **
    //**                                                        **
    //************************************************************
    //调用方法
    public function __call($method, $arguments)
    {
        if(in_array($method,self::$msSetting))
        {
            return call_user_func_array([self::$msInstance, '_' . $method], $arguments);
        }
        else if(in_array($method,self::$msLevel))
        {
            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
            self::$msInstance->setPutParameter($method,$backtrace);

            return call_user_func_array([self::$msInstance, '_writeLog'], $arguments);
        }
        else
        {
            //throw new \Exception("Method {$method} does not exist.");
        }
    }

    //调用静态方法
    public static function __callStatic($method, $arguments)
    {
        if(!self::$msInstance)
        {
            self::$msInstance = new static();
        }

        //清空文件名
        self::$msInstance->mFile = '';

        if (in_array($method, self::$msSetting))
        {
            return call_user_func_array([self::$msInstance, '_' . $method], $arguments);
        }
        elseif (in_array($method, self::$msLevel))
        {
            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);

            self::$msInstance->setPutParameter($method,$backtrace);
            return call_user_func_array([self::$msInstance, '_writeLog'], $arguments);
        }
        else
        {
            //throw new \Exception("Method {$method} does not exist.");
        }
    }
}
